﻿Imports System.IO
Imports System.Reflection

Public Class ObjectTypeProvider
    Inherits TypeProvider
    Public Property UseCustomVariables As Boolean
    Public Property IncludeFields As Boolean = True
    Public Property IncludeProperties As Boolean = False
    Public Property ExcludeNotSerialized As Boolean = True
    Protected Overridable Sub GetObjectVariables(Obj As Object, Vars As Dictionary(Of String, Object))
        Return 'Nothing
    End Sub
    Protected Overridable Sub SetObjectVariables(Obj As Object, Vars As Dictionary(Of String, Object))
        Return 'Nothing
    End Sub
    Protected Overridable Function FilterField(Obj As Object, F As FieldInfo) As Boolean
        If Not F.IsInitOnly And Not F.IsNotSerialized Then
            Return True
        Else
            Return False
        End If
    End Function
    Protected Overridable Function FilterProperty(Obj As Object, P As PropertyInfo) As Boolean
        If P.CanRead And P.CanWrite Then
            Return True
        Else
            Return False
        End If
    End Function
    Protected Overridable Sub SetProperty(Obj As Object, Prop As PropertyInfo, Value As Object)
        Prop.SetValue(Obj, Value)
    End Sub
    Public Overridable Function GetProperty(Obj As Object, P As PropertyInfo) As Object
        Return P.GetValue(Obj)
    End Function
    Public Overrides ReadOnly Property Types As Type()
        Get
            Return {GetType(Object)}
        End Get
    End Property
    Public Overrides Function ReadObject(T As Type) As Object
        Dim Obj = Activator.CreateInstance(T)
        Info.AddCache(Obj)
        If UseCustomVariables Then
            Dim D As New Dictionary(Of String, Object)
            Do
                Dim VarName = ReadVariableName()
                If VarName Is Nothing Then
                    Exit Do
                End If
                Using Sr As New StateReader(Info)
                    D.Add(VarName, Sr.DeserializeInternal(Reader))
                End Using
            Loop
            SetObjectVariables(Obj, D)
        Else
            Dim bf As BindingFlags = BindingFlags.CreateInstance Or BindingFlags.DeclaredOnly Or BindingFlags.Default Or BindingFlags.GetField Or BindingFlags.IgnoreCase Or BindingFlags.IgnoreReturn Or BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.SetField
            Dim bp As BindingFlags = BindingFlags.CreateInstance Or BindingFlags.DeclaredOnly Or BindingFlags.Default Or BindingFlags.GetProperty Or BindingFlags.IgnoreCase Or BindingFlags.IgnoreReturn Or BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.SetProperty
            Do
                Dim VarName = ReadVariableName()
                If VarName Is Nothing Then
                    Exit Do
                End If
                If IncludeFields Then
                    Dim F = T.GetField(VarName, bf)
                    If F IsNot Nothing Then
                        If FilterField(Obj, F) Then
                            Using Sr As New StateReader(Info)
                                F.SetValue(Obj, Sr.DeserializeInternal(Reader))
                            End Using
                        End If
                    End If
                End If
                If IncludeProperties Then
                    Dim P = T.GetProperty(VarName, bp)
                    If P IsNot Nothing Then
                        If FilterProperty(Obj, P) Then
                            Using Sr As New StateReader(Info)
                                SetProperty(Obj, P, Sr.DeserializeInternal(Reader))
                            End Using
                        End If
                    End If
                End If
            Loop
        End If
        Return Obj
    End Function
    Public Overrides Sub WriteObject(Obj As Object)
        Info.AddCache(Obj)
        If UseCustomVariables Then
            Dim D As New Dictionary(Of String, Object)
            GetObjectVariables(Obj, D)
            For Each kvp In D
                WriteVariableName(kvp.Key)
                Using wrt As New StateWriter(Info)
                    wrt.SerializeInternal(kvp.Value, Writer)
                End Using
            Next
        Else
            Dim bf As BindingFlags = BindingFlags.CreateInstance Or BindingFlags.DeclaredOnly Or BindingFlags.Default Or BindingFlags.GetField Or BindingFlags.IgnoreCase Or BindingFlags.IgnoreReturn Or BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.SetField
            Dim bp As BindingFlags = BindingFlags.CreateInstance Or BindingFlags.DeclaredOnly Or BindingFlags.Default Or BindingFlags.GetProperty Or BindingFlags.IgnoreCase Or BindingFlags.IgnoreReturn Or BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.SetProperty
            Dim Typ = Obj.GetType
            If IncludeFields Then
                For Each f As FieldInfo In Typ.GetFields(bf)
                    WriteVariableName(f.Name)
                    Using wrt As New StateWriter(Info)
                        wrt.SerializeInternal(f.GetValue(Obj), Writer)
                    End Using
                Next
            End If
            If IncludeProperties Then
                For Each P As PropertyInfo In Typ.GetProperties(bf)
                    WriteVariableName(P.Name)
                    Using wrt As New StateWriter(Info)
                        wrt.SerializeInternal(GetProperty(Obj, P), Writer)
                    End Using
                Next
            End If
        End If
    End Sub
End Class